Target :
Lucifer48 crackme 1 (d/l it on EB site : http://crackmes.cjb.net 
or on Lucifer's 48 site : http://www.multimania.com/lucifer48
Protection type :
Serial/Name protection, logical operations
Level :
Not as easy as Lucifer48 pretends :)
Tools needed :
SoftIce
A Brain
Some basis of cracking (recommended !!!)
Maths knowledge
Liters of beer
A collection of punk mp3


Before beginning...

                This crackme is based on a serial/name proctection. The serial is divided in two parts, separated by a dash '-'. The two parts are calculated in function of your name, and logical functions like XOR (mainly use although it rarely appears), OR, AND are heavily used, like ROR or SAR. Moreover, if you read the about box, you'll see that Lucifer48 qualifies his work as an "easy (logical) crackme". It's logical, that's right, but not so easy... time is money so let's go...


The essay...

                There are two textbox, one for the name, the other one for the serial, so we can use a bpx on getwindowtexta. Enter 'SiFLyiNG' in the first TextBox and '12345' in the second one.
Then press the 'Check' button and you're back in SoftIce. Press F5 another time and F11 to return to the caller and you should land here :

:004012FB              push ebx
:004012FC              push esi
:004012FD             push edi
:004012FE             push 00000016
:00401300             push 0040223B
:00401305             push dword ptr [00402018]

* Reference To: USER32.GetWindowTextA, Ord:0000h    ; this call gets the serial

:0040130B             Call 004014EC
:00401310             push 00000016
:00401312             push 00402223
:00401317             push dword ptr [00402013]

* Reference To: USER32.GetWindowTextA, Ord:0000h    ; this call gets the name

:0040131D             Call 004014EC
:00401322              xor edx, edx                       ; edx = 0       here you should land
:00401324             xor eax, eax                        ; eax = 0
:00401326             xor ecx, ecx                        ; ecx = 0
:00401328             xor ebx, ebx                        ; ebx = 0
:0040132A             mov cl, 0A                     ; cl = Ah
:0040132C             mov esi, 0040223B        ; esi = memory location of the serial

            This part of code is not really interesting. We only know that the name is at 40223B and the serial at 402223. Let's trace further and we enter in the first loop :

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401348(U)
|
:00401331             mov bl, byte ptr [esi]            ; move the char of the name in bl
:00401333             test bl, bl                                ; if bl =00 no more char left in the serial
:00401335             je 0040147F                            ; jmp to bad cracker if there are no more char left
:0040133B             cmp bl, 2D                             ; compares bl to 2D, hexa ascii value for the dash '-'
:0040133E             je 0040134A                           ; if equal jump out of the loop and go on checking
:00401340             mul ecx                                    ; these lines of
:00401342             and bl, 0F                               ;            code are converting the first part of the serial to
:00401345             add eax, ebx                            ;                        hexadecimal and the result is stored in eax
:00401347             inc esi                                    ; next char
:00401348             jmp 00401331                        ; loop again

            Ok, it becomes interesting. This loop test if the serial is divided in two parts separated by a dash. In our case, the entered serial is '12345' so it'll jump to the bad cracker routine because it contains no dash. Besides the end of the loop converts the first part of the serial to hexadecimal and stores the result in eax. We have to change the entered serial to '12345-67890' and come back in SoftIce. There, we can jump out of the loop, as the dash is found. You can even make a '? eax'  to see what eax contains : 3039. What's that ? Hey it's the first part of the serial converted to hexadecimal: 12345 = 3039h. Moreover we arrive here :

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040133E(C)
|
:0040134A push eax                                        ; stores the first part of the code
:0040134B xor eax, eax                                    ; eax = 0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040135C(U)
|
:0040134D          mov bl, byte ptr [esi+01]
:00401350          test bl, bl
:00401352         je 0040135E            ; jump out of the loop and go on checking
:00401354          mul ecx
:00401356         and bl, 0F
:00401359          add eax, ebx
:0040135B         inc esi
:0040135C          jmp 0040134D

            This loop converts the second part of the serial in hexadecimal. When there are no char left in the serial, it jumps out of the loop and goes on checking. Make a 'd eax' and you'll see :
10932h = 67890. This is the second part of the code. Go on tracing after the jump at 401352 and you arrive here :

:0040135E             mov edi, 00402223            ; move the memory location of the name in edi. type 'd edi' and you'll see the name
:00401363              mov cl, byte ptr [edi]        ; cl = char name
:00401365             test cl, cl                              ; is cl <> "" ??? <=> is name entered ?
:00401367             je 0040147F                        ; if not goto bad cracker routine
:0040136D             mov esi, eax                        ; esi = second part of code in hexa
:0040136F             mov ecx, FFFFFFFF            ; ecx = -1
:00401374             xor eax, eax                        ; eax = 0
:00401376             repnz
:00401377             scasb
:00401378             not ecx
:0040137A             dec ecx                            ; the previous line get the len of the name and store it un ecx ; ecx=8
:0040137B          pop eax                            ; pop the first part of code in eax
:0040137C          push ecx                         ; store the len of the name in ecx

            Then we enter in one of the most important loop of the crackme. It will transform the first part of the code.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040138F(C)
|
:0040137D 8BD8                    mov ebx, eax            ; ebx= eax
:0040137F D1EB                    shr ebx, 1                  ; ebx = SHR ebx, 1
:00401381 8BD3                    mov edx, ebx             ; edx = ebx = shr eax, 1
:00401383 F7D3                    not ebx                       ; ebx = not ebx = not(SHR eax, 1)
:00401385 23D8                    and ebx, eax               ; ebx = ebx AND eax = not(SHR eax, 1) AND NOT(SHR eax, 1)
:00401387 F7D0                    not eax                        ; eax = NOT eax
:00401389 23C2                    and eax, edx                ; eax AND edx
:0040138B 0BC3                    or eax, ebx                  ; eax = eax OR ebx
:0040138D FEC9                    dec cl                          ; cl = cl - 1
:0040138F 75EC                    jne 0040137D              ; loop until cl=0
 

** SHR shifts the destination right by "count" bits with zeroes shifted in on the left. (The carry flag contains the last bit shifted out), ie SHR EAX, 5 = EAX/(2^5)

        Waouhhhh... what's that ?!? It need some explanations... it seems hard but it's logical and we are often able to simplify what logical is. So let's try. First we should summarize what happens in this loop. We know that it depends on cl, and cl contains the lenght of the name so it will loop cl times, i.e. 8 times (because the lenght of 'SiFLyiNG' is 8). I'll summarize in a table what happen at each loop with our fake code :
 
 
1st loop 2nd loop 3rd loop 4th loop 5th loop 6th loop 7th loop 8th loop
cl
8
7
6
5
4
3
2
1
mov ebx, eax  ebx = 3039h ebx = 2825h ebx = 3C37h ebx = 222Ch ebx = 333Ah ebx = 2AA7h ebx = 3FF4h ebx = 200Eh
shr ebx, 1 (= ebx/2) ebx = 181Ch ebx = 1412h ebx = 1E1Bh ebx = 1116h ebx = 199Dh ebx = 1553h ebx = 1FFAh ebx = 1007h
and ebx, eax  ebx = 2021h ebx = 2825h ebx = 2024h ebx = 2228h ebx = 2222h ebx = 2AA4h ebx = 2004h ebx = 2008h
and eax, edx eax = 804h eax = 1412h eax = 208h eax = 1112h eax = 885h eax = 1550h eax = 0Ah eax = 1001h
or eax, ebx eax = 2825h eax = 3C37h eax = 222Ch eax = 333Ah eax = 2AA7h eax = 3FF4h eax = 200Eh eax = 3009h
dec cl 
7
6
5
4
3
2
1
0 (so no jump)
eax
 2825h
3C37h
222Ch
333Ah
2AA7h
3FF4h
200Eh
3009h

             Let's examine the loop and summarize what it does (we suppose that the serial is divided in two parts : X & Y so the serial is X-Y )

* 1st loop :

                    At line 401385 :
                                                        ebx = NOT (X/2h) AND X
                    then at line 40138B:
                                                        eax = NOT X AND (X/2h) OR ebx = NOT X AND (X/2) OR NOT (X/2) AND X
                    now use your brain : i use a picture to schematise ( i think it's more clear) :

** a bar on X means NOT X and a bar on (X/2) means NOT (X/2)



                All these lines for a XOR ;) So we can now simplify all the loops :
** some properties of the XOR function before: A XOR A = 0
                                                                                    (A XOR B ) XOR ( C XOR D) = A XOR B XOR C XOR D
                                                                                     A XOR B = B XOR A
 
1st loop X XOR (X/2)
2nd loop (X XOR X/2h) XOR (X XOR X/2h)/2h = X XOR X/2h XOR X/2h XOR X/4h = X XOR X/4h
3rd loop (X XOR X/4h) XOR (X XOR X/4h)/2h = X XOR X/4h XOR X/2h XOR X/8h
4th loop (X XOR X/4h XOR X/2h XOR X/8h) XOR (X XOR X/4h XOR X/2h XOR X/8h)/2
= X XOR X/4h XOR X/2h XOR X/8h XOR X/2h XOR X/8h XOR X/4h XOR X/10h = X XOR X/10h
5th loop (X XOR X/10h) XOR (X XOR X/10h)/2 = X XOR X/10h XOR X/2h XOR X/20h
6th loop (X XOR X/10h XOR X/2h XOR X/20h) XOR (X XOR X/10h XOR X/2h XOR X/20h)/2h
= X XOR X/10h XOR X/2h XOR X/20h XOR X/2h XOR X/20h XOR X/4h XOR/40h = X XOR X/10h XOR X/4h XOR X/40h
7th loop (X XOR X/10h XOR X/4h XOR X/40h) XOR (X XOR X/10h XOR X/4h XOR X/40h)/2h
= X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h 
8th loop (X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h) XOR (X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h)/2h = X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h XOR X/2 XOR X/20h XOR X/8h XOR X/80h XOR X/4 XOR X/40h XOR X/10h XOR X/100h
= X XOR (X/100h) because 2h^8h = 100h = 256

                So, all this loop is making is X XOR (X/100h) !!!
               This function depends on the lenght of the entered name : a 4-lenght-name would done : X XOR X/10h
                                                                                                                       a 7-lenght-name would done : X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h
                But in my case, as len = 8, the formula is X XOR X/100h. If you don't believe, just test all these formula with 3039h and you'll get the results of the first table !!!

Just a verification : 3039h XOR (SHR 3039h, 8) = 3039h XOR 3039h/100h = 3039h XOR 30h = 3009h
                                                                ^^                                ^^
If we had entered 123456h instead of 3039h it would have done : 123456h XOR (SHR 123456h, 8) = 123456h XOR (123456/100) = 123456h XOR 1234h
                                                                                                                  ^^^^                             ^^^^                                           ^^^^
                                or 98765432h it would have done 98765432h XOR (SHR 98765432h, 8) = 98765432h XOR 987654h

    I hope you believe the aim of this loop now...

So we can now trace and see what is after this loop :
 

:00401391            push eax        ; stores the result of the loop
:00401392            push esi       ; stores the memory location
:00401393             mov edi, 004021FF    ;
:00401398             dec edi                        ; edi points to "Greetings: all french crackers!"
:00401399             mov esi, 00402223    ; esi points to the name
:0040139E             mov cl, byte ptr [esi]    ; CL = first char of the name = 'S' = 53h
:004013A0          xor eax, eax                    ; eax = 0

    Then we enter in a new loop :

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004013AE(C)

:004013A2 mov edx, dword ptr [edi]        ; edx = dword from the string "Greetings: all french crackers!"
:004013A4 add eax, edx                            ; eax = eax + edx
:004013A6 rol eax, cl                                 ; ROL eax, cl = ROL eax, char from name
:004013A8 inc edi                                      ; next char in the greetings string
:004013A9 inc esi                                        ; next char in the name
:004013AA mov cl, byte ptr [esi]            ; cl = char from name
:004013AC test cl, cl                                   ; if cl=0 <=> no more char in name
:004013AE jne 004013A2                            ;         stop looping

    I won't explain this loop which could be useful for keygening the crackme. The most important to calculate the valid serial for 'SiFLyiNG' is to write down the result when there are no more char left in the name:

            EAX = C0991D44h for 'SiFLyiNG'

:004013B0            pop ecx                ; ecx = 2nd part of serial in hexa = Y
:004013B1             and eax, ecx        ; eax = eax AND eax = C0991D44h AND Y
:004013B3             mov ebx, ecx        ; ebx = Y
:004013B5             shr ecx, 1              ; ecx = Y/2h
:004013B7             not ecx                  ; ecx = NOT(Y/2h)
:004013B9             and ecx, ebx         ; ecx = NOT(Y/2h) AND Y
:004013BB             cmp eax, ecx        ; compare eax and ecx
:004013BD             jne 0040147C       ; if not equal => bad cracker
:004013C3            pop edi                   ; edi = 3009 = X XOR X/100h
:004013C4            pop ebx                   ; ebx = lenght of name
:004013C5             sub esi, ebx            ; esi = memory location of the name
:004013C7             mov eax, 00000948    ; eax = 948
:004013CC             mov edx, 00001048    ; edx = 1048

    Humm... another interesting part of code. This time there is a comparison between ecx got from Y (=the second part of the serial) and eax got from the name (=C0991D44h)
and the two registers must be equal, otherwise it jumps to the bad cracker routine...
    So what have we got ???

            ecx = NOT(Y/2h) AND Y
            eax = C0991D44h AND Y

and we must have : NOT(Y/2h) AND Y = C0991D44h AND Y
        <=> NOT(Y/2h) AND Y = C0991D44 AND Y
        <=> Y/2h = NOT C0991D44h     <=>     Y = 3F66E2BBh * 2 = 7ECDC576h = 2127414646

    Hey we have now calculated Y, i.e. the second part of the serial. Enter as serial '12345-2127414646' and come back at the comparison at 4013BB : you don't jump ;) So we can see the rest of code, another loop :

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004013DC(C)

:004013D1             mov cl, byte ptr [esi]   ; ESI points to the name so cl = char  from name
:004013D3             xor cl, bl                        ; cl= bl (at the beginning of the loop= lenght of name)  XOR cl
:004013D5             rol edx, cl                        ; ROL edx, cl
:004013D7             xor eax, edx                    ; eax = eax XOR edx
:004013D9             inc esi                            ; next char
:004013DA            dec bl                            ; is bl=0 ?
:004013DC             jne 004013D1                ; if not, then loop again

        We have finally at the end of this loop : EAX = 998440EAh for 'SiFLyiNG'

        And if you trace another time, you arrive at the last comparison routine :

:004013DE             cmp edi, eax        ; edi = 3009h and eax = 998440EAh
:004013E0              jne 0040147F        ; if they aren't equal go to bad cracker routine...

        The end comes... what is in EDI ??? 3009h ??? where does it come from ??? remember the loop from 40137D to 40138F, which is only equal to a X XOR X/100h It's compared to eax, which was calculated from the name only... so the result of the loop from 40137D to 40138F should have been equal to 998440EAh to win the jackpot. So we want here : edi = 998440EAh.
How to make ??? hehe... simple ;)

    We know that we must have : X XOR X/100h = 998440EAh

and remember the previous examples :
            3039h XOR (SHR 3039h, 8) = 3039h XOR 3039h/100h = 3039h XOR 30h = 3009h
            123456h XOR (SHR 123456h, 8) = 123456h XOR (123456/100) = 123456h XOR 1234h

for the last examples it's like :

                               12  34  56                            or                                30   39
                    XOR         12  34                                               XOR               30

Do you follow me ??? i hope...    so we should have if X = AA BB CC DD

                                   AA  BB    CC   DD
                    XOR               AA   BB   CC
                          ---------------------------------
                        =         99    84        40    EA

so AA =  99 = 99
        BB = 84 XOR AA = 84 XOR 99 = 1D
        CC = 40 XOR BB = 40 XOR 1D =  5D
        DD = EA XOR CC = EA XOR 5D = B7

    Hey !!! we know now AA BB CC DD, ie we know X which is the second part of code : X = 991D5DB7h = 2568838583 in decimal

SO WE KNOW NOW THE WHOLE SERIAL ;)

    Name : SiFLyiNG
    Serial :  2568838583-2127414646

        Disable all your breakpoints and try it : "Congratulations, you did it The power of the XOR is in you! Please mail me your solution/keygen at lucifer48@yahoo.com"


The end...

                Pffff... this crackme was very long to understand but if you look closer, once you have understood it's "easy". I won't make a keygen because i'm not good enough at asm, but if someone is brave enough to make it, please send me the source : i need to progress in asm... if you have questions, critics, if i have made errors or anything else, just mail me.

                    SiFLyiNG
                                    siflying@ifrance.com

Greetings : Lucifer48 of course :)
                    Acid Burn, Eternal Bliss, Skymarshall, Carpathia, Magic Raphoun... etc... etc